/*******************************************************************************
 * Author  : Son Hua
 * Date    : 25/08/2009
 * Description	:	Define global math functions (imitate shader math library)
 ******************************************************************************/

#ifndef _RENZO_MATH_
#define _RENZO_MATH_

#include "RenzoMustHave.h"
#include "RenzoPrecompiled.h"

// Use extern keyword to avoid C++ decoration
namespace Renzo {
//extern "C" {
	#define RADIAN_PER_DEGREE 0.0174532777777778 // 3.14159 / 180
	#define DEGREE_PER_RADIAN 57.29582790879777 // 180 / 3.14159
	#define PI 3.14159
	#define RADIAN(x) ((x) * RADIAN_PER_DEGREE)
	#define DEGREE(x) ((x) * DEGREE_PER_RADIAN)

	inline Float4& rzVectorNormalize(const Float4& v) {
		return Float4(v).normalize();
	}
	inline float rzVectorDot(const Float4& f1, const Float4& f2) {
		return f1.dot(f2);
	}

	//------------------------------------------------------------------------------
	// Vector utility
	//------------------------------------------------------------------------------
	inline Float3 rzMax3(const Float3& f1, const Float3& f2) {
		return Float3(max(f1.x, f2.x), max(f1.y, f2.y), max(f1.z, f2.z));
	}
	inline Float3 rzMin3(const Float3& f1, const Float3& f2) {
		return Float3(min(f1.x, f2.x), min(f1.y, f2.y), min(f1.z, f2.z));
	}

	//------------------------------------------------------------------------------
	// Matrix multiplication
	//------------------------------------------------------------------------------
	inline Matrix4 rzMatrixMultiply(const Matrix4& a, const Matrix4& b) {
		Matrix4 c;
		/*
		int i, j;
		for (i = 0; i < 4; i++) {
			for (j = 0; j < 4; j++) {
				c[i*4+j] =
					a[i*4+0]*b[0*4+j] +
					a[i*4+1]*b[1*4+j] +
					a[i*4+2]*b[2*4+j] +
					a[i*4+3]*b[3*4+j];
			}
		}*/

		c[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
		c[4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7];
		c[8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11];
		c[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15];
		c[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3];
		c[5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7];
		c[9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11];
		c[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15];
		c[2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3];
		c[6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7];
		c[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11];
		c[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15];
		c[3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3];
		c[7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7];
		c[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11];
		c[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15];

		/*
		for (int i = 0; i < 4; ++i) 
			for (int j = 0; j < 4; ++j) 
				printf("c[%d] = a[%d]*b[%d] + a[%d]*b[%d] + a[%d]*b[%d] + a[%d]*b[%d];\n", j * 4 + i, 0 * 4 + i, j * 4 + 0, 1 * 4 + i, j * 4 + 1, 2 * 4 + i, j * 4 + 2, 3 * 4 + i, j * 4 + 3);
		*/

		return c;
	}
	//------------------------------------------------------------------------------
	// Matrix scale
	//------------------------------------------------------------------------------
	inline Matrix4 rzMatrixMakeScale(const Float3& s) {
		Matrix4 m;
		m[0]	= s.x;
		m[5]	= s.y;
		m[10]	= s.z;
		m[15]	= 1.0f;
		return m;
	}

	//------------------------------------------------------------------------------
	// Matrix translation
	//------------------------------------------------------------------------------
	/**
	 * Translate a point
	 */
	inline Matrix4 rzMatrixMakeTranslate(const Float3& t) {
		Matrix4 m = Matrix4::Identity();
		m[12] = t.x;
		m[13] = t.y;
		m[14] = t.z;
		m[15] = 1.0f;
		return m;
	}

	//------------------------------------------------------------------------------
	// Matrix rotation
	//------------------------------------------------------------------------------
	inline Matrix4 rzMatrixMakeRotateZ(float r) {
		Matrix4 m;
		float cosr = cosf(r);
		float sinr = sinf(r);

		m[0] = cosr;	m[4] = -sinr;	m[8] = 0.0f;	m[12] = 0.0f;
		m[1] = sinr;	m[5] = cosr;	m[9] = 0.0f;	m[13] = 0.0f;
		m[2] = 0.0f;	m[6] = 0.0f;	m[10] = 1.0f;	m[14] = 0.0f;
		m[3] = 0.0f;	m[7] = 0.0f;	m[11] = 0.0f;	m[15] = 1.0f;

		return m;
	}

	inline Matrix4 rzMatrixMakeRotateY(float r) {
		Matrix4 m;
		float cosr = cosf(r);
		float sinr = sinf(r);

		m[0] = cosr;	m[4] = 0.0f;	m[8] = sinr;	m[12] = 0.0f;
		m[1] = 0.0f;	m[5] = 1.0f;	m[9] = 0.0f;	m[13] = 0.0f;
		m[2] = -sinr;	m[6] = 0.0f;	m[10] = cosr;	m[14] = 0.0f;
		m[3] = 0.0f;	m[7] = 0.0f;	m[11] = 0.0f;	m[15] = 1.0f;

		return m;
	}

	inline Matrix4 rzMatrixMakeRotateX(float r) {
		Matrix4 m;
		float cosr = cosf(r);
		float sinr = sinf(r);

		m[0] = 1.0f;	m[4] = 0.0f;	m[8] = 0.0f;	m[12] = 0.0f;
		m[1] = 0.0f;	m[5] = cosr;	m[9] = -sinr;	m[13] = 0.0f;
		m[2] = 0.0f;	m[6] = sinr;	m[10] = cosr;	m[14] = 0.0f;
		m[3] = 0.0f;	m[7] = 0.0f;	m[11] = 0.0f;	m[15] = 1.0f;

		return m;
	}

	inline Matrix4 rzMatrixMakeRotate(const Float3& r) {
		return rzMatrixMakeRotateX(r.x) * rzMatrixMakeRotateY(r.y) * rzMatrixMakeRotateZ(r.z);
	}

	//------------------------------------------------------------------------------
	// Matrix - vector operation
	//------------------------------------------------------------------------------
	// Matrix * vector
	inline Float4 rzVectorTransform(const Matrix4& m, const Float4& v) {
		// TODO: this matrix does not make good use of cache coherence (access m[0] then m[4]...)
		Float4 r;
		r[0] = m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12] * v[3];
		r[1] = m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13] * v[3];
		r[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];
		r[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
		return r;
	}

	// TODO: provide this overload needs a Math class
	/*
	inline Float3 rzVectorTransform(const Matrix4& m, const Float3& v) {
		Float3 r;
		return r;
	}*/

	inline Float3 rzPointTransform(const Matrix4& m, const Float3& v) {
		// Point, so w = 1
		// TODO: this matrix does not make good use of cache coherence (access m[0] then m[4]...)
		Float3 r;
		float r3 = 1.0f / (m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15]);
		r[0] = r3*(m[0] * v[0] + m[4] * v[1] + m[8] * v[2] + m[12]);
		r[1] = r3*(m[1] * v[0] + m[5] * v[1] + m[9] * v[2] + m[13]);
		r[2] = r3*(m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14]);
		return r;
	}
//}
}

/*
//#define sin(x) (float)sin(x)
//#define cos(x) (float)cos(x)
#define rad(x) (float)(x * M_PI / 180)
class Vector3f; // forward declaration
class Math
{
public:
	Math();
	virtual ~Math();
	
	static int Power(int x, int y);
	static float Power(float x, int y);
	static void Swap(int& a, int& b);
	static void MultiplyMatrix(float res[], float a[], float b[]);
	static void TransformVector(float matrix[16], float vector[4], float res[4]);
	static void RotateX(float angle, float matrix[]);
	static void RotateY(float angle, float matrix[]);
	static void RotateZ(float angle, float matrix[]);
	static void Translate(Vector3f pos, float matrix[]);
	static void Transpose(float a[16], float b[16]);
	static void MatrixIdentity(float matrix[]);

	static float dot(const Float3& a, const Float3& b);
	static float dot(const Float4& a, const Float4& b);
	static Float4 power(const Float4& f, float e);
	static Float4 power(const Float4& f, const Float4& e);	
};
*/

#endif 
